package edge.display.backend.MsgHandler;

import com.alibaba.fastjson.JSONObject;
import edge.display.backend.Entity.*;
import edge.display.backend.KEY_STORE;
import edge.display.backend.MsgDistributer.WebSocket.WebSocketServer;
import edge.display.backend.Utils.StatUtils;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;

/**
 * 网络类数据处理中心
 *
 * @author Amane Hayaashi
 * @date 2020/10/28
 * @since 1.0
 */


@Component
public class NetHandler {

    private static NetStat netStat;

    static {
        netStat = new NetStat();
        netStat.init();
    }

    // id, network, nodes, links
    public static void onComNet(JSONObject jsonObject) throws IOException {
        ComNet comNet = jsonObject.toJavaObject(ComNet.class);
        if (Objects.isNull(comNet)) {
            System.out.println("[NetHandler] Input ComNet is invalid!");
            return;
        }
        List<Node> nodes = comNet.getNodes();
        List<Link> links = comNet.getLinks();
        Network network = new Network();
        // rearrange id
        comNet.setId("" + comNet.getId().substring(0, 13));
        // rearrange network
        updateNetwork(network, nodes, links);
        comNet.setNetwork(network);
        // rearrange node
        updateNodes(nodes);
        comNet.setNodes(nodes);
        // rearrange link
        updateLinks(links);
        comNet.setLinks(links);
        // update stat
        netStat.update(comNet);
        // redis
        // AsyncRedisSave asyncRedisSave = new AsyncRedisSave();
        // asyncRedisSave.forEntity(comNet, KEY_STORE.REDIS_NET);
        // WebSocket
        JSONObject resJSONObject = new JSONObject(new HashMap<String, Object>() {
            {
                put(KEY_STORE.JSON_KEY, KEY_STORE.WS_STAT_NET);
                put(KEY_STORE.JSON_VALUE, netStat);
            }
        });
        WebSocketServer.broadcast(resJSONObject);
        System.out.println("[NetHandler] final ComNet:" + comNet.toString());
    }

    private static void updateNetwork(Network network, List<Node> nodes, List<Link> links) {
        double sumComputing = nodes.parallelStream().mapToDouble(n -> n.getRestComputing()[0]).sum();
        double sumStorage = nodes.parallelStream().mapToDouble(n -> n.getRestStorage()[0]).sum();
        network.setComputing(StatUtils.doubleRound2(
                nodes.parallelStream().mapToDouble(n -> n.getRestComputing()[2] * n.getRestComputing()[0] / sumComputing).sum() * 100));
        network.setStorage(StatUtils.doubleRound2(
                nodes.parallelStream().mapToDouble(n -> n.getRestStorage()[0] * n.getRestStorage()[1] / sumStorage).sum() * 100));
        network.setRate(StatUtils.doubleRound2(
                links.parallelStream().mapToDouble(Link::getRate).average().orElse(0.0f)));
        network.setEsDelay(StatUtils.doubleRound2(
                links.parallelStream().mapToDouble(Link::getEsDelay).average().orElse(0.0f)));
    }

    private static void updateNodes(List<Node> nodes) {
        for (Node node : nodes) {
            node.setId(KEY_STORE.NODE_MAP.getOrDefault(node.getIp(), -1));
            node.setIp(null);
            double[] restComputing = node.getRestComputing();
            double[] restStorage = node.getRestStorage();
            restComputing[2] = StatUtils.doubleRound2( restComputing[2] * 100);
            restStorage[1] = StatUtils.doubleRound2( restStorage[1] * 100);
            node.setRestComputing(restComputing);
            node.setRestStorage(restStorage);
        }
    }

    private static void updateLinks(List<Link> links) {
        for (Link link : links) {
            link.setNodeSrc(KEY_STORE.NODE_MAP.getOrDefault(link.getNodeFrom(), -1));
            link.setNodeDes(KEY_STORE.NODE_MAP.getOrDefault(link.getNodeTo(), -1));
            link.setRate(StatUtils.doubleRound2(link.getRate()));
            link.setNodeFrom(null);
            link.setNodeTo(null);
        }
    }
}
